#pragma once
#include <string>
#include <memory>
#include <map>
#include <functional>
#include <list>
#include <mutex>
#include "rtbus/rtbus.h"
#include "can/canitf.h"
namespace rtbus {

class JkCan : public RtCommuItf
{
public:
    JkCan(bool is_scan = false);
    ~JkCan() final;
    int open(BusCfg cfg) override;
    void close() override;
    int active() override;
    int deactive() override;
    int register_sdo_handle(uint8_t slave_id, uint32_t write_req_frame_id, uint32_t read_req_frame_id, uint32_t resp_frame_id, size_t obj_id_size) override;
    ssize_t send_without_check(CANItf::FrameData* frame, size_t max_cnt);
    void send() override;
    void recv(int64_t* timeoff) override;
    void reset() override;
    bool is_actived() override { return actived_flag_; }
    void collect_can_data(bool is_tx, CANItf::FrameData* frame, size_t cnt);
    /**
     * @brief Get the sdo object
     * 
     * @param slave_id 从站号
     * @param obj_id 对象编号，不同的类型的设备有不同的组织方式，在实际报文中高字节在前低字节在后，即0x1234的objid在报文中为0x12 0x34
     * @param cb 当收到frameid匹配的包时调用。必须在内部检查slaveid和objid，若不符需要返回非0，以继续等待匹配数据
     * @param timeout 发送请求后等待响应的时间，注意是真正发送出报文后的时间，不包括在队列中等待的时间，<=0为一直等待，单位ms，默认为80ms
     * @param retry 超时后重新发送的次数，<0为一直重发, 0为不重发，默认为3次
     * @return int 0为成功，-1为有重复的报文
     * @note 对于timeout和retry要慎重设置，过小可能导致sdo失败，过大可能导致sdo队列阻塞
     */
    int get_sdo(uint8_t slave_id, uint32_t obj_id, std::function<int(uint8_t*, int)> cb, int timeout = 80, int retry = 3) override;

    int get_sdo_blocking(uint8_t slave_id, uint32_t objid, void* data, int data_len, int retry);

    /**
     * @brief Get the sdo object
     * 
     * @param slave_id 从站号
     * @param obj_id 对象编号，不同的类型的设备有不同的组织方式，在实际报文中高字节在前低字节在后，即0x1234的objid在报文中为0x12 0x34
     * @param cb 当收到frameid匹配的包时调用。必须在内部检查slaveid和objid，若不符需要返回非0，以继续等待匹配数据
     * @param is_em 暂停当前sdo，插入紧急发送
     * @param timeout 发送请求后等待响应的时间，注意是真正发送出报文后的时间，不包括在队列中等待的时间，<=0为一直等待，单位ms，默认为80ms
     * @param retry 超时后重新发送的次数，<0为一直重发, 0为不重发，默认为3次
     * @return int 0为成功，-1为有重复的报文
     * @note 对于timeout和retry要慎重设置，过小可能导致sdo失败，过大可能导致sdo队列阻塞
     */
    int set_sdo(uint8_t slave_id, uint32_t obj_id, uint8_t val[4], size_t size, std::function<int(uint8_t*, int)> cb, int timeout = 80, int retry = 3);

    bool is_can() override { return true; }

private:
    struct SdoInfo;
    SdoInfo* get_sdo_req_data();
    bool has_same_sdo(SdoInfo& a);

private:
    std::shared_ptr<CANItf> drv_;
    struct SdoFrame
    {
        uint8_t slave_id;
        uint32_t read_req_id, write_req_id;
        uint32_t resp_id;
        size_t obj_id_size;
    };
    std::map<uint8_t, SdoFrame> sdo_frame_map_;
    struct SdoInfo
    {
        uint8_t slave_id;
        uint32_t obj_id;
        bool is_write;

        std::function<int(uint8_t*, int)> cb;
        int timeout;
        int retry;
        int timeout_cnt;
        int intervals;  //完成一次后等待的周期数,仅对注册的周期性sdo读有效

        uint32_t frame_req_id;
        uint32_t frame_resp_id;
        uint8_t frame_data[8];
        size_t frame_size;
        size_t frame_obj_size;
    };
    std::list<std::shared_ptr<SdoInfo>> sdo_list_;
    std::shared_ptr<SdoInfo> cur_sdo_;
    std::mutex sdo_mtx_;
    uint32_t cyclic_time_us_;
    bool actived_flag_;
};
}  // namespace rtbus
